# @desc 感知器, 线性单元
# @url https://www.zybuluo.com/hanbingtao/note/448086
require 'Perceptron'
class LinearUnit < Perceptron
  attr_accessor :weights, :bias

  def initialize input_num, activator
    super input_num, activator
  end

  def to_s
    "weights\t:#{@weights}\nbias\t:#{@bias}"
  end

  def predict input_vec
    sum = 0
    @weights.each_with_index do |i, index|
      sum += i * input_vec[index]
    end
    @activator.activate(sum + @bias)
  end

  def train input_vecs, labels, iteration, rate
    iteration.times do |i|
      one_iteration input_vecs, labels, rate
    end
  end

  def one_iteration input_vecs, labels, rate
    samples = []
    input_vecs.each_with_index do |v, index|
      samples.push([v, labels[index]])
    end
    samples.each do |a|
      out_put = predict(a[0])
      update_weights(a[0], out_put, a[1], rate)
    end
  end

  def update_weights input_vec, out_put, label, rate
    delta = label - out_put
    @weights.each_with_index do |v, index|
      @weights[index] += delta * input_vec[index] * rate
    end
    @bias += rate * delta
  end
end

def get_training_dataset
  input_vecs = [[5], [3], [8], [1.4], [10.1]]
  labels = [5500, 2300, 7600, 1800, 11400]
  return input_vecs, labels
end


def main times = 10, rate = 0.01
  linear_unit = LinearUnit.new(1, Activator)
  input_vecs, labels = get_training_dataset
  linear_unit.train(input_vecs, labels, times, rate)
  # puts linear_unit

  # puts "3.4 => #{linear_unit.predict [3.4]}"
  # puts "15 => #{linear_unit.predict [15]}"
  # puts "1.5 => #{linear_unit.predict [1.5]}"
  # puts "6.3 => #{linear_unit.predict [6.3]}"
  linear_unit.weights
end

def test max_times, min_times = 1, rate = 0.01
  liner_units = []
  test_ok = false
  file = File.new('res.text', 'a')
  Thread.new do
    max_times.times do |i|
      next if i < min_times
      liner_units.push((main i, rate).to_s + '\n' + i.to_s)
    end
    test_ok = true
  end
  loop do
    break if test_ok
    puts liner_units.size
    sleep 3
  end
  puts 'write to file, length:' + liner_units.size.to_s
  # liner_units.each do |r|
  #   file.puts r.to_s
  # end
  file.puts liner_units
end

class Activator
  def self.activate x
    x
  end
end